// Initial File - Paul Jack


#ifndef __ASYNCMGRBASE_H__
#define __ASYNCMGRBASE_H__


// Qt Includes
#include <qglobal.h>
#include <qobject.h>
#include <qbytearray.h>
#include <qmutex.h>
#include <qobject.h>
#include <qlist.h>

#include "commdevice.h"
#include "threadbase.h"

#include "messagequeue.h"

#define UNUSED(p) ((void)(p))
#define MAX_BUFFER_SIZE     512
#define MAX_BULK_RETRIES    5


/// Messages that can be passed to RMnemonicStream
enum
{
    EDebugEvent = 1000, ECallbackEvent, EResponseEvent, EWriteEvent,
        ECommunicationErrorEvent, EDisableEventFilter, EDisconnectEvent,
        EReadBulkEvent, ETimeoutBulkEvent, EReconnectEvent
};




class ReadThread;
class ReadBulkThread;
class WriteThread;
class WorkerInterface;


class NullCommDevice: public CommDevice
{
    bool write(const QByteArray &str) 
        {
            UNUSED(str);
            return false;
        };
    bool write(const QByteArray &str, bool sysCommand = false) 
        {   
            UNUSED(str);
            UNUSED(sysCommand);
            return false;
        };
    bool readBulk(QByteArray &array) 
        {
            UNUSED(array);
            return false;
        };
    bool read(QByteArray &str) 
        {
            UNUSED(str);
            return false;
        };
    bool isConnected (void) 
        {
            return false;
        };
    void setConnected(bool) {}; 
    void initialize( void ){};

};



class AsyncMgrBase: public QObject
{

    Q_OBJECT

public:
    /// Initialize the class with a device name to connect to.
    AsyncMgrBase(QString localName = QString("LocalName"), QString remoteName = QString("RemoteName"), QString deviceId = QString( "DeviceId"), quint32 bufferSize = MAX_BUFFER_SIZE);

    virtual ~AsyncMgrBase();

    bool connect();

    void disconnect();

    bool isConnected();

    void stop();

    QString getError();


    /// Return device name
    QString getDeviceName();

    /// Return USB product type
    QString getProduct();
    virtual bool isBulkDataSupported();

    /// Return USB manufacturer info
    QString getVendor();
    bool isMsCallbackEnabled();

    /// A method for retrieving Bulk Transfer data.
    /// @todo This should probably be restructured and renamed.
    int grabBulkData(char *data, int maxSize, int timeout);

    bool identifyDevice();

    virtual void initialize(){};
    
    void start();

    void startBulk();

    bool stopBulk();

    // Look, several writes all with the same name!  But, they use different arguments.
    // They just ensure that we are sending down a pointer to a QByteArray
    // 
    virtual bool write(const char *str, int size);
    virtual bool write(QString str);
    virtual bool write(const QByteArray &str);
    //virtual bool write(QByteArray str);


    /// Read response to query
    /// @param timeout Specified in ms. A negative value will wait
    ///     indefinitely.
    /// @return false if the timeout expires, otherwise true.
    bool read(QByteArray &str, int timeout =  - 1);

    /// Read bulk data from the device. 'Bulk' data is defined differently
    /// depending on the device you are reading from.
    /// @param timeout Specified in ms. A negative value will wait
    ///     indefinitely.
    /// @return false if the timeout expires or bulk data is not supported,
    ///     otherwise true.
    bool readBulk(QByteArray &array, int timeout =  - 1);

    virtual QString getLastKey(){return QString();};
    virtual QVariant getLastValue(){return QVariant();};


public slots:

    

signals: 
    void communicationError(const QString &error);
    void receivingBulk(bool state, QByteArray data);

protected:

    void setReadError(const QString &str);

    void setWriteError(const QString &str);

    CommDevice *mDevice;
    NullCommDevice mNullDevice;
    QByteArray mBuffer;
    QByteArray *mByteArray;
    quint32 mBufferSize;
    bool mNeedNewBuffer;




private:
    inline void forwardData(char data);

    bool isRunning();

    void setMsCallbackEnable(bool enabled);



    /// This method should never be called directly.
    bool readLoop();

    /// This method should never be called directly.
    bool readBulkLoop();

    /// This method should never be called directly.
    bool writeLoop();


protected:
    bool mBulkSupported;
    QMutex mMutex;
    bool mEnableMsCallback;
    bool mRunning;
    ThreadBase mReadBulkThread;
    ThreadBase mReadThread;
    ThreadBase mWriteThread;
    WorkerInterface *mpReadBulkThreadInterface;
    WorkerInterface *mpReadThreadInterface;
    WorkerInterface *mpWriteThreadInterface;

    MessageQueue<AsyncMgrBase> *mpReadQueue;
    MessageQueue<AsyncMgrBase> *mpWriteQueue;


    QByteArray mReadString;
    QString mReadBulkError;
    QString mReadError;
    QString mWriteError;
    friend class ReadThread;
    friend class ReadBulkThread;
    friend class WriteThread;

    /// A total count of bytes sent.
    unsigned long mBytesSent;

    /// A total count of bytes received.
    unsigned long mBytesReceived;

    QByteArray mOutBuffer;
    int mStringSize;

    int mDescLength;
    QString mDescType;
    QString mBcdUsb;
    QString mDeviceClass;
    QString mDeviceSubClass;
    QString mDeviceProtocol;
    QString mMaxPacketSize0;
    QString mIdVendor;
    QString mIdProduct;
    QString mBcdDevice;
    QString mManufacturer;
    QString mProduct;
    QString mIdentification;
    QString mLocalName;
    QString mRemoteName;
    QString mNumConfigurations;

    bool    mAttached;    



};




/*!
 * AsyncMgrBase::isMsCallbackEnabled 
 *
 * Description: 
 *
 *
 * @returns inline bool 
 */
inline bool AsyncMgrBase::isMsCallbackEnabled()
{
    bool result;
    mMutex.lock();
    result = mEnableMsCallback;
    mMutex.unlock();
    return result;
}



/*!
 * AsyncMgrBase::setReadError 
 *
 * Description: 
 *
 * @param &str is of type const QString 
 *
 * @returns inline void 
 */
inline void AsyncMgrBase::setReadError(const QString &str)
{
    QMutexLocker locker(&mMutex);
    mReadError = str;
}



/*!
 * AsyncMgrBase::setWriteError 
 *
 * Description: 
 *
 * @param &str is of type const QString 
 *
 * @returns inline void 
 */
inline void AsyncMgrBase::setWriteError(const QString &str)
{
    QMutexLocker locker(&mMutex);
    mWriteError = str;
}



#endif __ASYNCMGRBASE_H__
